package com.vcc.core.advice;

import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotSafeException;
import cn.hutool.core.text.StrBuilder;
import com.vcc.core.annotations.RestPack;
import com.vcc.core.exception.NotFoundException;
import com.vcc.core.exception.PermissionException;
import com.vcc.core.exception.ProgramException;
import com.vcc.core.exception.SignInException;
import com.vcc.core.model.Result;
import com.vcc.core.model.StatusEnum;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import org.springframework.web.util.ContentCachingRequestWrapper;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.sql.SQLException;
import java.util.UUID;

@ControllerAdvice
public class GlobalHandler implements ResponseBodyAdvice<Object> {

    private static final Logger logger = LoggerFactory.getLogger(GlobalHandler.class);

    @Override
    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
        Method method = methodParameter.getMethod();
        return method != null && method.isAnnotationPresent(RestPack.class);
    }

    @Override
    public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        return (o instanceof Result) ? (Result<?>) o : Result.returnGenEriCity(o);
    }



    @ResponseBody
    @ExceptionHandler({RuntimeException.class})
    public Result<Object> exceptionHandler(RuntimeException e) {
        logger.error("出现RuntimeException异常", e);
        disposeExceptionRequestMessage();
        return Result.returnError(StatusEnum.PRO_ERROR.getStatus(), e.getMessage());
    }

    @ResponseBody
    @ExceptionHandler({NotPermissionException.class})
    public Result<Object> notPermissionExceptionHandler(NotPermissionException e) {
        logger.error("出现NotPermissionException异常", e);
        disposeExceptionRequestMessage();
        return Result.returnError(StatusEnum.PRO_ERROR.getStatus(), "您当前没有此操作权限");
    }

    @ResponseBody
    @ExceptionHandler({NullPointerException.class})
    public Result<Object> nullPointerExceptionHandler(NullPointerException e) {
        logger.error("出现NullPointerException异常", e);
        disposeExceptionRequestMessage();
        return Result.returnError(StatusEnum.PRO_ERROR.getStatus(), "系统异常，请稍后再试");
    }

    @ResponseBody
    @ExceptionHandler({NotSafeException.class})
    public Result<Object> notSafeExceptionHandler(NotSafeException e) {
        logger.error("出现NotSafeException异常", e);
        disposeExceptionRequestMessage();
        switch (e.getCode()) {
            case 11071:
                return Result.returnError(11071, "需要二次验证");
            default:
                break;
        }
        return Result.returnError(StatusEnum.PRO_ERROR.getStatus(), "系统异常，请稍后再试");
    }

    @ResponseBody
    @ExceptionHandler({NotLoginException.class})
    public Result<Object> notLoginExceptionHandler(NotLoginException e) {
        logger.error("出现NotLoginException异常", e);
        disposeExceptionRequestMessage();
        return Result.returnError(StatusEnum.SIGN_ERROR.getStatus(), "登陆失效");
    }

    @ResponseBody
    @ExceptionHandler({SQLException.class})
    public Result<Object> sqlExceptionHandler(SQLException e) {
        logger.error("出现SQLException异常", e);
        disposeExceptionRequestMessage();
        return Result.returnError(StatusEnum.PRO_ERROR.getStatus(), "系统异常，请稍后再试");
    }

    @ResponseBody
    @ExceptionHandler({NotFoundException.class})
    public Result<Object> notFoundExceptionHandler(NotFoundException e) {
        logger.error("出现NotFoundException异常", e);
        disposeExceptionRequestMessage();
        return Result.returnError(e.getStatus(), e.getMessage());
    }

    @ResponseBody
    @ExceptionHandler({PermissionException.class})
    public Result<Object> permissionExceptionHandler(PermissionException e) {
        logger.error("出现PermissionException异常", e);
        disposeExceptionRequestMessage();
        return Result.returnError(e.getStatus(), e.getMessage());
    }

    @ResponseBody
    @ExceptionHandler({SignInException.class})
    public Result<Object> signInExceptionHandler(SignInException e) {
        logger.error("出现SignInException异常", e);
        disposeExceptionRequestMessage();
        
        return Result.returnError(e.getStatus(), e.getMessage());
    }

    @ResponseBody
    @ExceptionHandler({ProgramException.class})
    public Result<Object> programExceptionHandler(ProgramException e) {
        logger.error("出现ProgramException异常", e);
        disposeExceptionRequestMessage();
        
        return Result.returnError(e.getStatus(), e.getMessage());
    }

    @ResponseBody
    @ExceptionHandler({Exception.class})
    public Result<Object> exceptionHandler(Exception e) {
        logger.error("出现Exception异常", e);
        disposeExceptionRequestMessage();
        return Result.returnError(StatusEnum.PRO_ERROR.getStatus(), e.getMessage());
    }

    @ExceptionHandler({BindException.class})
    @ResponseBody
    public Result<Object> exceptionHandler500(BindException e) {
       StrBuilder strBuilder = StrBuilder.create();
        //只抛出一个异常信息就结束
        for (ObjectError objectError : e.getAllErrors()) {
            strBuilder.append(objectError.getDefaultMessage()).append(",");
        }
        logger.error("参数异常，异常信息:{}",strBuilder, e);
        disposeExceptionRequestMessage();
        String uuid = UUID.randomUUID().toString();
        logger.error("uuid:{},Bind异常：", uuid, e);
        return Result.returnError(StatusEnum.PRO_ERROR.getStatus(), strBuilder.toString().substring(0, strBuilder.toString().length() - 1));
    }

    @ExceptionHandler({ConstraintViolationException.class})
    @ResponseBody
    public Result<Object> exceptionHandler500(ConstraintViolationException e) {
        StrBuilder stringBuilder = StrBuilder.create();
        for (ConstraintViolation<?> constraintViolation : e.getConstraintViolations()) {
            stringBuilder.append(constraintViolation.getMessage()).append(",");
        }
        logger.error("参数异常,异常信息：{}",stringBuilder.toString(), e);
        disposeExceptionRequestMessage();
        String uuid = UUID.randomUUID().toString();
        logger.error("uuid:{},Violation异常：", uuid, e);
        return Result.returnError(StatusEnum.PRO_ERROR.getStatus(), stringBuilder.toString().substring(0, stringBuilder.toString().length() - 1));
    }

    private void disposeExceptionRequestMessage() {
        ServletRequestAttributes servletRequestAttributes = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes());
        if (servletRequestAttributes == null) {
            return;
        }
        HttpServletRequest request = servletRequestAttributes.getRequest();
        logger.error("请求地址为{}",request.getServletPath());
        logger.error("请求参数为{}",request.getQueryString());
        logger.error("请求方法为{}",request.getMethod());
        if (request.getMethod().equals("POST")) {
            // 读取body参数
            // 这里不能直接使用request.getInputStream()，否则会导致后续无法读取body
            // 可以考虑使用过滤器将body缓存下来，或者使用Spring提供的ContentCachingRequestWrapper


        }

    }


}
